package com.derschmale.wick3d.core.io.collada
{
	import com.derschmale.wick3d.core.io.collada.data.ColladaAsset;
	import com.derschmale.wick3d.core.io.collada.data.ColladaGeometry;
	import com.derschmale.wick3d.materials.ColourMaterial;
	import com.derschmale.wick3d.materials.TextureMaterial;
	
	import flash.display.Bitmap;
	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	
	public class DAEParser extends EventDispatcher
	{
		private var _loader : URLLoader;
		private var _bitmapLoader : Loader;
		private var _xml : XML;
		private var _path : String;
		private var _filename : String;
		
		private var _asset : ColladaAsset;
		
		private var _geometries : Array;
		private var _bitmapData : Array;
		private var _materialObjectLookUp : Array = [];
		private var _materials : Array = [];
		private var _models : Array;
		
		private var _numImages : int;
		private var _imageCount : int;
		
		public function DAEParser(filename : String)
		{
			var slashOccurence : int = filename.lastIndexOf("/");
			
			if (slashOccurence == -1) slashOccurence = filename.lastIndexOf("\\");
			if (slashOccurence != -1) _path = filename.substr(0, slashOccurence+1);
			else _path = "";

			_loader = new URLLoader();
			_loader.addEventListener(Event.COMPLETE, handleDAELoadComplete);
			_loader.load(new URLRequest(filename));
			
			_filename = filename;
			
			trace ("Loading DAE file: "+filename);
		}
		
		public function get models() : Array
		{
			return _models;
		}
		
		private function handleDAELoadComplete(event : Event) : void
		{
			trace ("Loaded DAE file: "+_filename);
			var temp : String = String(_loader.data);
			// fry, namespace, fry!
			temp = temp.replace(new RegExp("xmlns[^\"]*\"[^\"]*\"", "gi"), "");
			
			_xml = new XML(temp);
			
			_asset = new ColladaAsset(XML(_xml.asset));
			parseGeometryLibrary(XML(_xml.library_geometries));
			parseScene();
			parseImages();
		}
		
		private function parseGeometryLibrary(library : XML) : void
		{
			var list : XMLList = library.geometry;
			_geometries = new Array();
			
			for (var i : int = 0; i < list.length(); i++) {
				_geometries[list[i].@id] = new ColladaGeometry(list[i]);
			}
		}
		
		private function parseScene() : void
		{
			var sceneId : String = String(_xml.scene.instance_visual_scene.@url).substr(1);
			var visualScene : XML = XML(_xml.library_visual_scenes.visual_scene.(@id==sceneId));
			var nodes : XMLList = visualScene.node;
			
			_models = new Array();
			
			for (var i : int = 0; i < nodes.length(); i++) {
				parseSceneNode(nodes[i]);
			}
			trace ("parsed scene "+sceneId);
		}
		
		private function parseSceneNode(node : XML, parent : DAEModel = null) : void
		{
			if (node.hasOwnProperty("instance_geometry")) {
				
				var model : DAEModel = translateModel(node.instance_geometry, node.@id, node.@name);
				var translate : Array, scale : Array, rotate : Array;
				
				if (model) {
					if (node.hasOwnProperty("translate")) {
						translate = String(node.translate).split(" ");
						model.x = translate[0];
						model.y = translate[1];
						model.z = translate[2];
					}
					if (node.hasOwnProperty("scale")) {
						scale = String(node.scale).split(" ");
						model.scaleX = scale[0];
						model.scaleY = scale[1];
						model.scaleZ = scale[2];
					}
					/* if (node.hasOwnProperty("rotate")) {
						rotate = String(node.rotate).split(" ");
						model.rotationX = rotate[0];
						model.rotationY = rotate[1];
						model.rotationZ = rotate[2];
					} */
					
					if (!parent) _models.push(model);
					trace ("parsed scene node "+node.@name);
				}
			}
		}
		
		private function translateModel(geometry : XMLList, id : String, name : String) : DAEModel
		{
			var geometryId : String = String(geometry.@url).substr(1);
			var model3D : DAEModel;
			
			if (ColladaGeometry(_geometries[geometryId]).numTriangles) {
				model3D = new DAEModel(_geometries[geometryId], id, name)
				
				if (geometry.hasOwnProperty("bind_material")) {
					var matId : String = String(geometry.bind_material.technique_common.instance_material.(@symbol==ColladaGeometry(_geometries[geometryId]).material).@target).substr(1);
					_materialObjectLookUp[matId] = model3D;
				}
				
				else _materialObjectLookUp[ColladaGeometry(_geometries[geometryId]).material] = model3D;
				return model3D;
			}
			
			return null;
		}
		
		private function parseImages() : void
		{
			_bitmapData = new Array();
			_numImages = _xml.library_images.image.length();
			loadImage(_xml.library_images.image[0]);
		}
		
		private function handleImageLoaded(event : Event) : void
		{
			_bitmapData[_xml.library_images.image[_imageCount].@id] = Bitmap(_bitmapLoader.content).bitmapData;
			_bitmapLoader.removeEventListener(Event.COMPLETE, handleImageLoaded);
			_imageCount++;
			
			if (_imageCount == _numImages) {
				parseMaterials();
			}
			else loadImage(_xml.library_images.image[_imageCount]);
		}
		
		private function loadImage(node : XML) : void
		{
			var filename : String = _path+node.init_from;
			trace ("loading image "+filename);
			_bitmapLoader = new Loader();
			_bitmapLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleImageLoaded);
			_bitmapLoader.load(new URLRequest(filename));
		}
		
		private function parseMaterials() : void
		{
			var materials : XMLList = _xml.library_materials.material;
			var id : String;
			var instanceId : String;
			var instance : XMLList;
			
			for (var i : int = 0; i < materials.length(); i++) {
				id = materials[i].@id;
				instanceId = String(materials[i].instance_effect.@url).substr(1);
				instance = _xml.library_effects.effect.(@id == instanceId);
				generateMaterial(id, instance);
			}
			
			applyMaterials();
			trace("Materials parsed");
		}
		
		private function generateMaterial(id : String, effectNode : XMLList) : void
		{
			var tech : XMLList = effectNode.profile_COMMON.technique.(@sid=="common");
			var shade : String;
			var texId : String;
			
			if (tech.hasOwnProperty("phong")) shade = "phong";
			else if (tech.hasOwnProperty("blinn")) shade = "blinn";
			
			if (tech[shade].diffuse.hasOwnProperty("texture")) {
				texId = tech[shade].diffuse.texture.@texture;
				texId = effectNode.profile_COMMON.newparam.(@sid==texId).sampler2D.source;
				texId = effectNode.profile_COMMON.newparam.(@sid==texId).surface.init_from;
				
				_materials[id] = new TextureMaterial(_bitmapData[texId]);
			}
			else {
				// fix:
				_materials[id] = new ColourMaterial(0xff00ff);
			}
		}
		
		private function applyMaterials() : void
		{
			for (var id : String in _materialObjectLookUp) {
				DAEModel(_materialObjectLookUp[id]).material = _materials[id];
			}
			dispatchEvent(new Event(Event.COMPLETE));
		}
	}
}